home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Peter Lewis / PNL Libraries / MyEditObject.p < prev    next >
Encoding:
Text File  |  1994-08-27  |  16.7 KB  |  630 lines  |  [TEXT/PJMM]

  1. unit MyEditObject;
  2.  
  3. interface
  4.  
  5.     type
  6.         EditObject = object
  7.                 window: dialogPtr;
  8.                 titem: integer;
  9.                 vcontrol, hcontrol: controlHandle;
  10.                 te: TEHandle;
  11.                 titemr: rect;
  12.                 hasgrow, drawgrow: boolean; { hasgrow -> leave room for grow icon, drawgrow -> draw it during updates }
  13.                 doubleClickTime, tripleClickTime: longInt;
  14.                 readonly: boolean;
  15.                 undotext: handle;
  16.                 undostart, undoend, undoselstart, undoselend: integer;
  17.                 undoopen: boolean;
  18.                 modified: boolean;
  19.                 procedure Create (dlg: dialogPtr; item, width: integer; vscroll, hscroll, hasgrowb, drawgrowb, static: boolean);
  20.                 procedure Destroy;
  21.                 procedure Adjust;
  22.                 procedure Resize;
  23.                 procedure Draw;
  24.                 function EditMenuEnabled: boolean;
  25.                 procedure SetEditMenuItem (item: integer);
  26.                 procedure DoEditMenu (item: integer);
  27.                 procedure DoItemWhere (er: eventRecord; item: integer);
  28.                 procedure DoIdle;
  29.                 procedure DoKey (modifiers: integer; ch: char);
  30.                 procedure DoActivateDeactivate (activate: boolean);
  31.                 procedure ClickLoop;
  32.                 procedure Click (pt: point; extend: boolean);
  33.                 function WordBreak (text: ptr; pos: integer; forward: boolean): boolean;
  34.             end;
  35.  
  36. implementation
  37.  
  38.     uses
  39.         MyOOMainLoop, BaseGlobals, MyTypes, MyUtils, MyUtilities;
  40.  
  41.     var
  42.         teo: EditObject;
  43.         teOriginalClickLoop: procPtr;
  44.  
  45. { DON'T EVEN THINK ABOUT LOOKING AT THIS CODE!!!!! }
  46.  
  47.     procedure CallCL (addr: procPtr);
  48.     inline
  49.         $205F, $4E90;
  50.  
  51.     procedure SetD0to1;
  52.     inline
  53.         $7001;
  54.  
  55.     function GetD2: longInt;
  56.     inline
  57.         $2F42, $0000;
  58.  
  59.     procedure Unlink;
  60.     inline
  61.         $4E5E;
  62.  
  63.     procedure Link;
  64.     inline
  65.         $4E56, $0000;
  66.  
  67. {$PUSH}
  68. {$D-}
  69.   { Turn debug off, lest our qute little SetD0to1 hack gets crunged by TP }
  70.     procedure CallClickLoop;  { There must be a better way to sort out this crap! }
  71.     begin
  72.         Unlink;  { This is a rediculous hack! }
  73.         CallCL(teOriginalClickLoop);
  74.         Link;
  75.         teo.ClickLoop;
  76.         SetD0to1;
  77.     end;
  78.  
  79.     function CallWordBreak (text: ptr; pos: integer): boolean;
  80.         var
  81.             d2: longInt;
  82.     begin
  83.         d2 := GetD2;
  84.         CallWordBreak := teo.WordBreak(text, pos, BAND(d2, $00020000) = 0);
  85.     end;
  86. {$POP}
  87.  
  88.     function FindEOL (te: TEHandle; loc: integer): integer;
  89.     begin
  90.         while (loc < te^^.teLength) and (ptr(longInt(te^^.hText^) + loc)^ <> 13) do
  91.             loc := loc + 1;
  92.         FindEOL := loc;
  93.     end;
  94.  
  95.     procedure EditObject.Click (pt: point; extend: boolean);
  96.         var
  97.             tc, dct: longInt;
  98.             doubleclick, tripleclick: boolean;
  99.             teOriginalWordBreak: procPtr;
  100.             eol: integer;
  101.     begin
  102.         SetPort(window);
  103.         tc := TickCount;
  104.         doubleclick := tc < doubleClickTime;
  105.         tripleclick := tc < tripleClickTime;
  106.         teo := self;
  107.         teOriginalClickLoop := te^^.clikLoop;
  108.         te^^.clikLoop := @CallClickLoop;
  109.         teOriginalWordBreak := te^^.wordBreak;
  110.         if tripleclick then
  111.             SetWordBreak(@CallWordBreak, te);
  112.         if extend and tripleclick then begin{ we must fake text edit into not shrinking the selection somehow }
  113.             eol := FindEOL(te, te^^.selStart);  { if start<=clickloc<=EOL(start)<selEnd }
  114.             if (te^^.selStart <= te^^.clickloc) and (te^^.clickloc <= eol) and (eol < te^^.selEnd) then
  115.                 TESetSelect(te^^.clickloc, te^^.selEnd, te);
  116.         end;
  117.         TEClick(pt, extend, te);
  118.         tc := TickCount;
  119.         dct := GetDblTime;
  120.         doubleClickTime := tc + dct;
  121.         if doubleclick then
  122.             tripleClickTime := tc + dct;
  123.         te^^.clikLoop := teOriginalClickLoop;
  124.         te^^.wordBreak := teOriginalWordBreak;
  125.  
  126.         if readonly and (te^^.selStart = te^^.selEnd) then begin  { kludge to make the carret go away }
  127.             TEDeactivate(te);
  128.             TEActivate(te);
  129.         end;
  130.  
  131.     end;
  132.  
  133.     function DirtyKey (ch: char): boolean;
  134.     begin
  135.         case ord(ch) of
  136.             homeChar, endChar, helpChar, pageUpChar, pageDownChar, leftArrowChar, rightArrowChar, upArrowChar, downArrowChar: 
  137.                 DirtyKey := false;
  138.             otherwise
  139.                 DirtyKey := true;
  140.         end;
  141.     end;
  142.  
  143.     procedure EditObject.Create (dlg: dialogPtr; item, width: integer; vscroll, hscroll, hasgrowb, drawgrowb, static: boolean);
  144.         var
  145.             dr, vr: rect;
  146.             k: integer;
  147.             h: handle;
  148.     begin
  149.         readonly := static;
  150.         doubleClickTime := -1;
  151.         tripleClickTime := -1;
  152.         SetPort(dlg);
  153.         window := dlg;
  154.         titem := item;
  155.         hasgrow := hasgrowb;
  156.         drawgrow := drawgrowb;
  157.         if vscroll then begin
  158.             SetRect(dr, 0, 0, 16, 100);
  159.             vcontrol := NewControl(window, dr, '', true, 0, 0, 0, scrollBarProc, 0);
  160.         end
  161.         else
  162.             vcontrol := nil;
  163.         if hscroll then begin
  164.             SetRect(dr, 0, 0, 100, 16);
  165.             hcontrol := NewControl(window, dr, '', true, 0, 0, 0, scrollBarProc, 0);
  166.         end
  167.         else
  168.             hcontrol := nil;
  169.         GetDItem(dlg, titem, k, h, dr);
  170.         titemr := dr;
  171.         EraseRect(dr);
  172.         vr := dr;
  173.         dr.right := dr.left + width;
  174.         te := TENew(dr, vr);
  175.         TEAutoView(true, te);
  176.         undotext := NewHandle(0);
  177.         undostart := -1;
  178.         undoopen := false;
  179.         modified := false;
  180.         Resize;
  181.     end;
  182.  
  183.     procedure EditObject.Destroy;
  184.     begin
  185.         TEDispose(te);
  186.         DisposHandle(undotext);
  187.         dispose(self);
  188.     end;
  189.  
  190.     procedure AdjustTE (te: TEHandle; hc, vc: integer);
  191. {Scroll the TERec around to match up to the potentially updated scrollbar}
  192. {values. This is really useful when the window resizes such that the}
  193. {scrollbars become inactive and the TERec had been previously scrolled.}
  194.     begin
  195.         with te^^ do
  196.             TEScroll((viewRect.left - destRect.left) - hc, (viewRect.top - destRect.top) - (vc * lineHeight), te);
  197.     end; {AdjustTE}
  198.  
  199.     function AdjustHV (isVert: BOOLEAN; control: ControlHandle; te: TEHandle; canRedraw: BOOLEAN): integer;
  200. {Calculate the new control maximum value and current value, whether it is the horizontal or}
  201. {vertical scrollbar. The vertical max is calculated by comparing the number of lines to the}
  202. {vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document}
  203. {width to the width of the viewRect. The current values are set by comparing the offset between}
  204. {the view and destination rects. If necessary and we canRedraw, have the control be re-drawn by}
  205. {calling ShowControl.}
  206.         var
  207.             value, lines, max: INTEGER;
  208.             oldValue, oldMax: INTEGER;
  209.             cliprgn: RgnHandle;
  210.             r: rect;
  211.     begin
  212.         oldValue := GetCtlValue(control);
  213.         oldMax := GetCtlMax(control);
  214.         with te^^ do begin
  215.             if isVert then begin
  216.                 lines := nLines;
  217.         {since nLines isn’t right if the last character is a return, check for that case}
  218.                 if (teLength > 0) & (Ptr(ORD(hText^) + teLength - 1)^ = 13) then
  219.                     lines := lines + 1;
  220.                 max := lines - ((viewRect.bottom - viewRect.top) div lineHeight);
  221.             end
  222.             else
  223.                 max := destRect.right - destRect.left - (viewRect.right - viewRect.left);
  224.             if max < 0 then
  225.                 max := 0;            {check for negative values}
  226.             if isVert then
  227.                 value := (viewRect.top - destRect.top) div lineHeight
  228.             else
  229.                 value := viewRect.left - destRect.left;
  230.             if value < 0 then
  231.                 value := 0
  232.             else if value > max then
  233.                 value := max;                    {pin the value to within range}
  234.         end;
  235.         SetPort(te^^.inPort);
  236.         cliprgn := NewRgn;
  237.         GetClip(clipRgn);
  238.         SetRect(r, 0, 0, 0, 0);
  239.         ClipRect(r);
  240.         SetCtlMax(control, max);
  241.         SetClip(clipRgn);
  242.         DisposeRgn(clipRgn);
  243.         SetCtlValue(control, value);
  244.         if canRedraw and ((max <> oldMax) or (value <> oldValue)) then
  245.             ShowControl(control);            {check to see if the control can be re-drawn}
  246.         AdjustHV := value;
  247.     end; {AdjustHV}
  248.  
  249.     procedure EditObject.Adjust;
  250.         var
  251.             hc, vc: integer;
  252.     begin
  253.         vc := AdjustHV(true, vcontrol, te, false);
  254.         hc := AdjustHV(false, hcontrol, te, false);
  255.         AdjustTE(te, hc, vc);
  256.     end; {AdjustScrollValues}
  257.  
  258.     procedure EditObject.Resize;
  259.         const
  260.             invis = 0;
  261.             vis = 255;
  262.             inset = 3;
  263.         var
  264.             vr, tr: rect;
  265.             pt: point;
  266.             k: integer;
  267.             h: handle;
  268.             ht: integer;
  269.             hc, vc: integer;
  270.     begin
  271.         SetPort(window);
  272.         EraseRect(titemr);
  273.         GetDItem(window, titem, k, h, tr);
  274.         titemr := tr;
  275.         InvalRect(tr);
  276.         vr := tr;
  277.         InsetRect(vr, inset, inset);
  278.         if hcontrol <> nil then
  279.             vr.bottom := vr.bottom - 15;
  280.         if vcontrol <> nil then
  281.             vr.right := vr.right - 15;
  282.         vr.bottom := vr.top + (vr.bottom - vr.top) div te^^.lineHeight * te^^.lineHeight;
  283.  
  284.         pt := vr.topleft;
  285.         SubPt(te^^.viewRect.topleft, pt);
  286.         OffsetRect(te^^.destRect, pt.h, pt.v);
  287.  
  288.         te^^.viewRect := vr;
  289.  
  290.         if vcontrol <> nil then begin
  291.             vcontrol^^.contrlVis := invis;
  292.             MoveControl(vcontrol, tr.right - 16, tr.top);
  293.             ht := tr.bottom - tr.top;
  294.             if hasgrow then
  295.                 ht := ht - 15;
  296.             SizeControl(vcontrol, 16, ht);
  297.             vc := AdjustHV(true, vcontrol, te, false);
  298.             vcontrol^^.contrlVis := vis;
  299.         end;
  300.         if hcontrol <> nil then begin
  301.             hcontrol^^.contrlVis := invis;
  302.             MoveControl(hcontrol, tr.left, tr.bottom - 16);
  303.             ht := tr.right - tr.left;
  304.             if hasgrow or (vcontrol <> nil) then
  305.                 ht := ht - 15;
  306.             SizeControl(hcontrol, ht, 16);
  307.             hc := AdjustHV(false, hcontrol, te, false);
  308.             hcontrol^^.contrlVis := vis;
  309.         end;
  310.         AdjustTE(te, hc, vc);
  311.     end;
  312.  
  313.     procedure EditObject.Draw;
  314.         var
  315.             r: rect;
  316.             k: integer;
  317.             h: handle;
  318.     begin
  319.         GetDItem(window, titem, k, h, r);
  320.         EraseRect(r);
  321.         if drawgrow then begin
  322.             DrawGrowIcon(window);
  323.         end;
  324.         if vcontrol <> nil then begin
  325.             Draw1Control(vcontrol);
  326.         end;
  327.         if hcontrol <> nil then begin
  328.             Draw1Control(hcontrol);
  329.         end;
  330.         EraseRect(te^^.viewRect);
  331.         TEUpdate(te^^.viewRect, te);
  332.     end;
  333.  
  334.     procedure EditObject.DoActivateDeactivate (activate: boolean);
  335.     begin
  336.         if drawgrow then
  337.             DrawGrowIcon(window);
  338.         if activate then
  339.             TEActivate(te)
  340.         else
  341.             TEDeactivate(te);
  342.     end;
  343.  
  344. { Common algorithm for pinning the value of a control. It returns the actual amount }
  345. { the value of the control changed. }
  346.     procedure CommonAction (control: ControlHandle; var amount: integer);
  347.         var
  348.             value, max: integer;
  349.     begin
  350.         value := GetCtlValue(control);
  351.         max := GetCtlMax(control);
  352.         amount := value - amount;
  353.         if (amount <= 0) then
  354.             amount := 0
  355.         else if (amount >= max) then
  356.             amount := max;
  357.         SetCtlValue(control, amount);
  358.         amount := value - amount;   { calculate true change }
  359.     end; { CommonAction  }
  360.  
  361.     var
  362.         actionTE: TEHandle;
  363.  
  364. { Determines how much to change the value of the vertical scrollbar by and how }
  365. { much to scroll the TE record.}
  366.     procedure VActionProc (control: ControlHandle; part: integer);
  367.         var
  368.             amount: integer;
  369.             window: WindowPtr;
  370.     begin
  371.         if (part <> 0) then begin
  372.             window := control^^.contrlOwner;
  373.             case part of
  374.                 inUpButton, inDownButton:        { one line  }
  375.                     amount := 1;
  376.                 inPageUp, inPageDown:            { one page  }
  377.                     with actionTE^^, viewRect do
  378.                         amount := (bottom - top) div lineHeight;
  379.             end;
  380.             if ((part = inDownButton) or (part = inPageDown)) then
  381.                 amount := -amount;        { reverse direction for a downer  }
  382.             CommonAction(control, amount);
  383.             if (amount <> 0) then
  384.                 TEScroll(0, amount * actionTE^^.lineHeight, actionTE);
  385.         end;
  386.     end; { VActionProc }
  387.  
  388. { Determines how much to change the value of the horizontal scrollbar by and how }
  389. { much to scroll the TE record. }
  390.     procedure HActionProc (control: ControlHandle; part: integer);
  391.         var
  392.             amount: integer;
  393.             window: WindowPtr;
  394.     begin
  395.         if (part <> 0) then begin
  396.             window := control^^.contrlOwner;
  397.             case part of
  398.                 inUpButton, inDownButton:        { a few pixels }
  399.                     amount := 8;
  400.                 inPageUp, inPageDown:            { a page width }
  401.                     with actionTE^^.viewRect do
  402.                         amount := (right - left);
  403.             end;
  404.             if ((part = inDownButton) or (part = inPageDown)) then
  405.                 amount := -amount;        { reverse direction }
  406.             CommonAction(control, amount);
  407.             if (amount <> 0) then
  408.                 TEScroll(amount, 0, actionTE);
  409.         end;
  410.     end; { HActionProc }
  411.  
  412. { Gets called from CallClickLoop which in turn }
  413. { is called by the TEClick toolbox routine. Saves the window's clip region, }
  414. { sets it to the portRect, adjusts the scrollbar values to match the TE scroll }
  415. { amount, then restores the clip region. }
  416.     procedure EditObject.ClickLoop;
  417.         var
  418.             region: RgnHandle;
  419.             vc, hc: integer;
  420.     begin
  421.         SetPort(window);
  422.         region := NewRgn;
  423.         GetClip(region);                { save the old clip }
  424.         ClipRect(window^.portRect);        { set the new clip }
  425.         vc := AdjustHV(true, vcontrol, te, false);
  426.         hc := AdjustHV(false, hcontrol, te, false);
  427.         SetClip(region);                { restore the old clip }
  428.         DisposeRgn(region);
  429.     end; { PascalClikLoop }
  430.  
  431.     function EditObject.WordBreak (text: ptr; pos: integer; forward: boolean): boolean;
  432.     begin
  433.         if forward then
  434.             WordBreak := (pos > 0) and (ptr(longInt(text) + pos - 1)^ = 13)
  435.         else
  436.             WordBreak := ptr(longInt(text) + pos)^ = 13
  437.     end;
  438.  
  439.     procedure EditObject.DoItemWhere (er: eventRecord; item: integer);
  440.         var
  441.             control: controlHandle;
  442.             value, part: integer;
  443.             uss, use: integer;
  444.     begin
  445.         uss := te^^.selStart;
  446.         use := te^^.selEnd;
  447.         SetPort(window);
  448.         GlobalToLocal(er.where);
  449.         part := FindControl(er.where, window, control);
  450.         if part = 0 then begin
  451.             if PtInRect(er.where, te^^.viewRect) then
  452.                 Click(er.where, BAND(er.modifiers, shiftKey) <> 0)
  453.         end
  454.         else begin
  455.             if part = inThumb then begin
  456.                 value := GetCtlValue(control);
  457.                 part := TrackControl(control, er.where, nil);
  458.                 if part <> 0 then begin
  459.                     value := value - GetCtlValue(control);
  460.                     if value <> 0 then
  461.                         if control = vcontrol then
  462.                             TEScroll(0, value * te^^.lineHeight, te)
  463.                         else
  464.                             TEScroll(value, 0, te);
  465.                 end;
  466.             end
  467.             else begin
  468.                 actionTE := te;
  469.                 if control = vcontrol then
  470.                     value := TrackControl(control, er.where, @VActionProc)
  471.                 else
  472.                     value := TrackControl(control, er.where, @HActionProc);
  473.             end;
  474.         end;
  475.         if (uss <> te^^.selStart) or (use <> te^^.selEnd) then
  476.             undoopen := false;
  477.     end;
  478.  
  479.     function EditObject.EditMenuEnabled: boolean;
  480.         var
  481.             i: integer;
  482.             offset: longInt;
  483.     begin
  484.         for i := EMundo to EMselectall do
  485.             if i <> EMundo + 1 then
  486.                 SetEditMenuItem(i);
  487.         EditMenuEnabled := false;
  488.         if (te^^.selStart < te^^.selEnd) or (te^^.teLength > 0) then { Select All, Copy }
  489.             EditMenuEnabled := true;
  490.         if not readonly and ((undostart >= 0) or (GetScrap(nil, 'TEXT', offset) > 0)) then { Undo, Paste }
  491.             EditMenuEnabled := true;
  492.     end;
  493.  
  494.     procedure EditObject.SetEditMenuItem (item: integer);
  495.         var
  496.             offset: longInt;
  497.     begin
  498.         case item of
  499.             EMundo: 
  500.                 SetIDItemEnable(M_Edit, item, undostart >= 0);
  501.             EMcut, EMclear: 
  502.                 SetIDItemEnable(M_Edit, item, not readonly and (te^^.selStart < te^^.selEnd));  { Can cut,clear iff there is a selection and its not readonly}
  503.             EMcopy: 
  504.                 SetIDItemEnable(M_Edit, item, te^^.selStart < te^^.selEnd);  { Can copy iff there is a selection }
  505.             EMpaste: 
  506.                 SetIDItemEnable(M_Edit, item, not readonly and (GetScrap(nil, 'TEXT', offset) > 0));        {Paste is enabled for app. windows}
  507.             EMselectall: 
  508.                 SetIDItemEnable(M_Edit, item, te^^.teLength > 0);  { Can select all iff there is something to select }
  509.             otherwise
  510.         end;
  511.     end;
  512.  
  513.     procedure CopyUndoSelection (h: handle; te: TEHandle);
  514.     begin
  515.         SetHandleSize(h, te^^.selEnd - te^^.selStart);
  516.         BlockMove(ptr(longInt(te^^.hText^) + te^^.selStart), h^, te^^.selEnd - te^^.selStart);
  517.     end;
  518.  
  519.     procedure EditObject.DoEditMenu (item: integer);
  520.         var
  521.             oe: OSErr;
  522.             loe: longInt;
  523.             th: handle;
  524.             uss, use: integer;
  525.     begin
  526.         undoopen := false;
  527.         case item of
  528.             EMcopy:  begin
  529.                 TECopy(te);
  530.                 loe := ZeroScrap;
  531.                 oe := TEToScrap;
  532.             end;
  533.             EMselectall:  begin
  534.                 SetPort(window);
  535.                 TESetSelect(0, maxLongInt, te);
  536.             end;
  537.             EMcut:  begin
  538.                 CopyUndoSelection(undotext, te);
  539.                 undoselstart := te^^.selStart;
  540.                 undoselend := te^^.selEnd;
  541.                 undostart := te^^.selStart;
  542.                 undoend := undostart;
  543.                 TECut(te);
  544.                 loe := ZeroScrap;
  545.                 oe := TEToScrap;
  546.                 modified := true;
  547.             end;
  548.             EMclear:  begin
  549.                 CopyUndoSelection(undotext, te);
  550.                 undoselstart := te^^.selStart;
  551.                 undoselend := te^^.selEnd;
  552.                 undostart := te^^.selStart;
  553.                 undoend := undostart;
  554.                 TEDelete(te);
  555.                 modified := true;
  556.             end;
  557.             EMpaste:  begin
  558.                 oe := TEFromScrap;
  559.                 if TEGetScrapLen + (te^^.teLength - (te^^.selEnd - te^^.selStart)) > 32000 then
  560.                     AlertUser(paste_to_big)
  561.                 else begin
  562.                     CopyUndoSelection(undotext, te);
  563.                     undoselstart := te^^.selStart;
  564.                     undoselend := te^^.selEnd;
  565.                     undostart := te^^.selStart;
  566.                     TEPaste(te);
  567.                     undoend := te^^.selEnd;
  568.                     modified := true;
  569.                 end;
  570.             end;
  571.             EMundo:  begin
  572.                 uss := undoselstart;
  573.                 use := undoselend;
  574.                 undoselstart := te^^.selStart;
  575.                 undoselend := te^^.selEnd;
  576.                 th := NewHandle(undoend - undostart);
  577.                 BlockMove(ptr(longInt(te^^.hText^) + undostart), th^, undoend - undostart); { save undo for redo }
  578.                 TESetSelect(undostart, undoend, te);
  579.                 TEDelete(te);
  580.                 HLock(undotext);
  581.                 TEInsert(undotext^, GetHandleSize(undotext), te);
  582.                 DisposHandle(undotext);
  583.                 undotext := th;
  584.                 undoend := te^^.selEnd;
  585.                 TESetSelect(uss, use, te);
  586.             end;
  587.             otherwise
  588.         end;
  589.     end;
  590.  
  591.     procedure EditObject.DoIdle;
  592.     begin
  593.         if not readonly then
  594.             TEIdle(te);
  595.     end;
  596.  
  597.     procedure EditObject.DoKey (modifiers: integer; ch: char);
  598.         procedure Doit;
  599.         begin
  600.             if BAND(modifiers, cmdKey) = 0 then
  601.                 TEKey(ch, te);
  602.             Adjust;
  603.         end;
  604.         var
  605.             dk: boolean;
  606.     begin
  607.         dk := DirtyKey(ch);
  608.         if dk then begin
  609.             if not readonly then begin
  610.                 modified := true;
  611.                 if not undoopen then begin
  612.                     CopyUndoSelection(undotext, te);
  613.                     undoselstart := te^^.selStart;
  614.                     undoselend := te^^.selEnd;
  615.                     undostart := te^^.selStart;
  616.                 end;
  617.                 Doit;
  618.                 undoend := te^^.selEnd;
  619.             end
  620.             else begin
  621.                 SysBeep(1);
  622.             end;
  623.         end
  624.         else begin
  625.             Doit;
  626.         end;
  627.         undoopen := dk;
  628.     end;
  629.  
  630. end.